﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content.Pipeline.Audio;

using CPXMenu;
using CPXMenuItem = CPXMenu.MenuItem;

using CPXMenuEditor.Forms;
using CPXMenuEditor.Properties;

namespace CPXMenuEditor.Controls
{
    public partial class ContentImporter : UserControl
    {
        MenuProject menuProject;
        string importFolder;
        Type importType;
        string assetName = string.Empty;
        List<object> importers = new List<object>();
        List<object> processors = new List<object>();
        List<Type> importerTypes = new List<Type>();
        List<Type> processorTypes = new List<Type>();

        public ContentImporter()
        {
            InitializeComponent();

            PopulateCombos();            
        }

        public void Initialize(MenuProject menuProject, string importFolder, Type importType)
        {
            this.menuProject = menuProject;
            this.importFolder = importFolder;
            this.importType = importType;            
        }

        private void PopulateCombos()
        {
            Type selectedImporter = null;
            Type selectedProcessor = null;

            if (importersCombo.SelectedItem != null)
                selectedImporter = importerTypes[importersCombo.SelectedIndex];
            if (processorsCombo.SelectedItem != null)
                selectedProcessor = processorTypes[processorsCombo.SelectedIndex];

            importers.Clear();
            processors.Clear();
            
            // Load known types
            importerTypes.Add(typeof(TextureImporter));
            importerTypes.Add(typeof(FontDescriptionImporter));              
            importerTypes.Add(typeof(EffectImporter));
            importerTypes.Add(typeof(XImporter));
            importerTypes.Add(typeof(FbxImporter));
            importerTypes.Add(typeof(XmlImporter));
            importerTypes.Add(typeof(WmvImporter));
            importerTypes.Add(typeof(WmaImporter));
            importerTypes.Add(typeof(Mp3Importer));
            importerTypes.Add(typeof(WavImporter));
            importerTypes.Add(typeof(XmlImporter));            

            processorTypes.Add(typeof(TextureProcessor));
            processorTypes.Add(typeof(EffectProcessor));
            processorTypes.Add(typeof(ModelProcessor));
            processorTypes.Add(typeof(VideoProcessor));
            processorTypes.Add(typeof(SoundEffectProcessor));
            processorTypes.Add(typeof(SongProcessor));
            processorTypes.Add(typeof(FontDescriptionProcessor));
            processorTypes.Add(typeof(FontTextureProcessor));
            processorTypes.Add(typeof(PassThroughProcessor));

            // Load custom importers and processors
            if (Settings.Default.PipelineAssemblies != null)
            {
                foreach (string assemblyPath in Settings.Default.PipelineAssemblies)
                {
                    try
                    {
                        Assembly assembly = Assembly.LoadFile(assemblyPath);

                        Type[] types = assembly.GetTypes();
                        foreach (Type type in types)
                        {
                            if (PipelineAssemblies.IsContentImporter(type))
                                importerTypes.Add(type);
                            else if (PipelineAssemblies.IsContentImporter(type))
                                processorTypes.Add(type);
                        }
                    }
                    catch (Exception)
                    {

                    }
                }
            }

            importers.Clear();
            processors.Clear();

            foreach (Type importerType in importerTypes)
                importers.Add(Activator.CreateInstance(importerType));
            foreach (Type processorType in processorTypes)
                processors.Add(Activator.CreateInstance(processorType));

            importersCombo.Items.Clear();
            processorsCombo.Items.Clear();

            foreach (Type importerType in importerTypes)
                importersCombo.Items.Add(GetImporterName(importerType));
            foreach (Type processorType in processorTypes)
                processorsCombo.Items.Add(GetProcessorName(processorType));

            if (selectedImporter != null)
                importersCombo.SelectedItem = GetImporterName(selectedImporter);
            if (selectedProcessor != null)
                processorsCombo.SelectedItem = GetProcessorName(selectedProcessor);
        }

        private string GetImporterName(Type importerType)
        {
            if (importerType == null)
                return string.Empty;

            object[] attributes = importerType.GetCustomAttributes(false);
            foreach (object attribute in attributes)
                if (attribute is ContentImporterAttribute)
                    return (attribute as ContentImporterAttribute).DisplayName;

            return importerType.Name;
        }

        private string GetProcessorName(Type processorType)
        {
            if (processorType == null)
                return string.Empty;

            object[] attributes = processorType.GetCustomAttributes(false);
            foreach (object attribute in attributes)
                if (attribute is ContentProcessorAttribute)
                    return (attribute as ContentProcessorAttribute).DisplayName;

            return processorType.Name;
        }

        private bool IsValidAssetName(string name)
        {
            char[] invalidChars = Path.GetInvalidFileNameChars();
            foreach (char invalidChar in invalidChars)            
                if (name.Contains(new string(invalidChar, 1)))
                    return false;
            return true;
        }

        private bool IsUniqueAssetName(string name)
        {
            if (string.IsNullOrEmpty(name))
                return true;

            foreach (ImportedContentItem contentItem in menuProject.ImportedContent)
                if (contentItem.Path.ToLower() == (importFolder + "\\" + name).ToLower())
                    return false;

            return true;
        }

        private string GetOpenFileFilter()
        {
            string filter = string.Empty;
            if (importType == typeof(Texture2D))
                filter = "Texture Files (*.bmp, *.png, *.jpg, *.tga, *.dds)|*.bmp;*.png;*.jpg;*.jpeg;*.tga;*.dds|";
            else if (importType == typeof(SpriteFont))
                filter = "SpriteFont Files (*.spritefont)|*.spritefont|";
            else if (importType == typeof(SoundEffect))
                filter = "Sound Files (*.mp3, *.wav, *.wma)|*.mp3;*.wav;*.wma|";
            else if (importType == typeof(Song))
                filter = "Music Files (*.mp3, *.wav, *.wma)|*.mp3;*.wav;*.wma|";
            else if (importType == typeof(Video))
                filter = "Video Files (*.wmv)|*.wmv|";
            else if (importType == typeof(Model))
                filter = "Model Files (*.fbx. *.x)|*.fbx;*.x|";
            else if (importType == typeof(Effect))
                filter = "Effect Files (*.fx)|*.fx|";

            filter += "All Files (*.*)|*.*";

            return filter;
        }        

        private void importersCombo_SelectedIndexChanged(object sender, EventArgs e)
        {
            importerParameters.SelectedObject = importers[importersCombo.SelectedIndex];
        }

        private void processorsCombo_SelectedIndexChanged(object sender, EventArgs e)
        {
            processorParameters.SelectedObject = processors[processorsCombo.SelectedIndex];
        }

        private void manageButton_Click(object sender, EventArgs e)
        {
            PipelineAssemblies dialog = new PipelineAssemblies();
            if (dialog.ShowDialog(this) == DialogResult.OK)
                PopulateCombos();
        }

        private void assetNameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (!IsValidAssetName(assetNameTextBox.Text))
            {
                MessageBox.Show(this, "Asset Name cannot contain the illegal file characters \""
                                    + new string(Path.GetInvalidFileNameChars()) + "\"", MainFrm.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                assetNameTextBox.Text = assetName;
                return;
            }

            if (!IsUniqueAssetName(assetNameTextBox.Text))
            {
                MessageBox.Show(this, "An imported asset already exists with this name", MainFrm.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                assetNameTextBox.Text = assetName;
                return;
            }
                          
            assetName = assetNameTextBox.Text;
        }

        private void browseButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.AddExtension = true;
            dialog.AutoUpgradeEnabled = true;
            dialog.CheckFileExists = true;
            dialog.CheckPathExists = true;
            dialog.FileName = sourceTextBox.Text;
            dialog.Filter = GetOpenFileFilter();
            dialog.Multiselect = false;
            dialog.SupportMultiDottedExtensions = true;

            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                sourceTextBox.Text = dialog.FileName;

                // Assign an asset name
                int count = 1;
                string baseAssetName = Path.GetFileNameWithoutExtension(sourceTextBox.Text); 
                string newAssetName = baseAssetName;
                while (!IsUniqueAssetName(newAssetName))
                {
                    newAssetName = baseAssetName + count;
                    count++;
                }

                assetNameTextBox.Text = newAssetName;

                Type selectedImporter = null;
                Type selectedProcessor = null;
                ContentImporterAttribute importerAttribute = null;
                string fileExtension = Path.GetExtension(sourceTextBox.Text).ToLower();

                // Assign a default importer and processor
                foreach(Type importerType in importerTypes)
                {
                    object[] attributes = importerType.GetCustomAttributes(false);
                    foreach (object attribute in attributes)
                    {
                        if (attribute is ContentImporterAttribute)
                        {
                            importerAttribute = attribute as ContentImporterAttribute;
                            foreach (string extension in importerAttribute.FileExtensions)
                            {
                                if (extension.ToLower() == fileExtension)
                                {
                                    selectedImporter = importerType;
                                    break;
                                }
                            }

                            if (selectedImporter != null)
                                break;
                        }
                    }

                    if (selectedImporter != null)
                    break;
                }                

                // If we've got an importer, try to select a processor
                if (selectedImporter != null)
                {
                    if (importerAttribute != null)
                    {
                        // Check for the Default Processor first
                        foreach (Type processorType in processorTypes)
                        {
                            if (processorType.Name == importerAttribute.DefaultProcessor)
                            {
                                selectedProcessor = processorType;
                                break;
                            }                                                     
                        }

                        // If we didn't find it, see if we can match up by the ouput type of the importer
                        if (selectedProcessor == null)
                        {
                            foreach (Type processorType in processorTypes)
                            {
                                Type[] importerArguments = selectedImporter.BaseType.GetGenericArguments();
                                Type[] processorArguments = processorType.BaseType.GetGenericArguments();
                                if (importerArguments.Length > 0 && processorArguments.Length > 0 && importerArguments[0] == processorArguments[0])
                                {
                                    selectedProcessor = processorType;
                                    break;
                                }
                            }
                        }
                    }
                }

                importersCombo.SelectedItem = GetImporterName(selectedImporter);
                processorsCombo.SelectedItem = GetProcessorName(selectedProcessor);
            }
        }       
    }
}
